import sys
import base64
#sys模块是与系统环境交互，用来处理Python运行的
#base64是用于加密环节
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QToolTip, QMessageBox, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel, QRadioButton, QTableWidget, QTextBrowser
from PyQt5.QtGui import QIcon, QFont
#QIcon和QFont分别用于设置突变和字体类型
from PyQt5.QtCore import *  # QCoreApplication
#用来对程序进行控制
from Crypto.Cipher import AES
#AES加密模块
#!/usr/bin/python
# -*- coding:utf-8 -*-
#登陆时的用户名和密码
Username_dic = {'sqh': '123',
                'Janet': '1234',
                'Thanos': 'Marvel',
                'Iron Man': 'Pepper'}

class Aescrypt():  # 定义一个加密的类
    def __init__(self, key, model, encode_):  # key是密钥，model直接设定为ECB，encode是编码模式，设定为'utf8'
        self.encode_ = encode_
        self.model = AES.MODE_ECB
        # 密钥如果不是16的倍数，需要经过一个补全处理，但是在后边给定的'keyskeyskeyskeys'不必进行此操作，本身即满足需求
        self.key = self.add_to_16s(key)
        self.aes = AES.new(self.key, self.model)  # 设定加密的模式，ECB模式没有iv偏移量，比较简单

    def add_to_16s(self, something):  # 定义补全的函数，设定其应该是16的倍数
        # 此处如果是汉字，则会编程utf8编码，否则将其转化为字节类型
        something = something.encode(self.encode_)
        while len(something) % 16 != 0:
            something += b'\x00'  # 如果位数不是16.32等16的倍数，要进行补全操作，在后边加上'\x00'
        return something

    def Aes_encrypt(self, text):  # 定义加密函数
        text = self.add_to_16s(text)  # 第一步用add_to_16s函数进行补全操作
        self.encrypted_text = self.aes.encrypt(text)
        # 此处返回的是一个加密完成的暗码
        return base64.encodebytes(self.encrypted_text).decode().strip()

    def Aes_decrypt(self, text):
        text = base64.decodebytes(text.encode(self.encode_))
        self.decrypt_text = self.aes.decrypt(text)
        return self.decrypt_text.decode(self.encode_).strip('\0')

#类的定义完成之后，定义两个函数，方便之后对明文进行加密处理，储存在本地和提取时将本地的暗码转化为明文

def encrypt(x):
    parameter = Aescrypt('keyskeyskeyskeys', 'ECB', 'utf8')
    return parameter.Aes_encrypt(x)


def decrypt(x):
    parameter = Aescrypt('keyskeyskeyskeys', 'ECB', 'utf8')
    return parameter.Aes_decrypt(x)


class MainWindow(QWidget):  # 定义一个主窗口的类，
    def __init__(self):
        super().__init__()  # super()表示新的MainWindow类是从Qwidget中继承而来的
        self.initUI()  # 之后会进行定义的函数

    def initUI(self):
        #设置提示语和标签的对应字体
        QToolTip.setFont(QFont('Microsoft YaHei', 12))
        QLabel.setFont(self, QFont('Microsoft YaHei', 15))

        #建立两个按钮，属于Qpushbutton类型，一个QRadiobutton类型，用于密码的明暗转换
        self.Button_show_password = QRadioButton('Show the password', self)
        self.Button_show_password.toggled.connect(self.Password_show)

        self.Button_Login = QPushButton('Log In', self)
        #Button_Login和Button_exit分别设置了提示符号，中间的<b>text</b>是markdown中的语法，表示加粗。（助教和老师们肯定知道，加这个注释的意思是表示并不是从网上直接抄过来的）
        #用connect将三个控件和对应的槽函数相连接
        self.Button_Login.setToolTip(
            'If you want to <b>log in</b>, click this button please')
        self.Button_Login.clicked.connect(self.Login_account)
        self.Button_exit = QPushButton('Exit', self)
        self.Button_exit.clicked.connect(QCoreApplication.instance().quit)
        self.Button_exit.setToolTip(
            'If you want to <b>quit</b> this boring and annoying program, click this button please')
        #建立标签和输入框
        self.Username_Label = QLabel('Username', self)
        self.Password_Label = QLabel('Password', self)
        self.Username_enter = QLineEdit(self)
        self.Password_enter = QLineEdit(self)
        #设置最初不进行任何操作下的默认状态是密码不可见
        self.Password_enter.setEchoMode(QLineEdit.Password)

        self.setToolTip('This is just a <b>normal window</b> created by SQH')
        #设置界面大小和出现时距离桌面起始点的距离
        self.setGeometry(400, 400, 450, 400*0.618)
        #设置标题
        self.setWindowTitle('Password Manager')
        #设置窗口图标
        self.setWindowIcon(QIcon('BUAA.jpg'))

        #调用函数，对输入框进行一个提示操作
        self.Input_state()
        #调用函数，布局
        self.Place_init()

        self.show()

    def Input_state(self):
        #输入框中的提示语句
        self.Username_enter.setPlaceholderText('Please Enter your Username')
        self.Password_enter.setPlaceholderText('Please Enter your Password')
        self.Username_enter.textChanged.connect(self.Check_input_func)
        self.Password_enter.textChanged.connect(self.Check_input_func)

    def Password_show(self):
        #密码的明暗转换，Normal是正常形式，Password是加密形式
        if self.Button_show_password.isChecked():
            self.Password_enter.setEchoMode(QLineEdit.Normal)
        else:
            self.Password_enter.setEchoMode(QLineEdit.Password)

    def Check_input_func(self):
        #如果输入框都为空的话，login功能将不可用
        if self.Username_enter.text() and self.Password_enter.text():
            self.Button_Login.setEnabled(True)
        else:
            self.Button_Login.setEnabled(False)

    def Place_init(self):
        #创建布局，H代表水平布局，V代表垂直布局
        self.V_Label_place = QVBoxLayout()
        self.V_Enter_place = QVBoxLayout()
        self.H_Button_place = QHBoxLayout()
        self.H_RadioButton_place = QHBoxLayout()
        self.H_Label_Enter_place = QHBoxLayout()
        self.V_Whole_place = QVBoxLayout()

        #在布局中添加各种控件（按照先后顺序）
        self.V_Label_place.addWidget(self.Username_Label)
        self.V_Label_place.addWidget(self.Password_Label)
        self.V_Enter_place.addWidget(self.Username_enter)
        self.V_Enter_place.addWidget(self.Password_enter)
        self.H_Button_place.addWidget(self.Button_Login)
        self.H_Button_place.addWidget(self.Button_exit)
        self.H_RadioButton_place.addWidget(
            self.Button_show_password, alignment=Qt.AlignHCenter)
        #对整体的模块排布，横向上先排标签，再排文本框
        self.H_Label_Enter_place.addLayout(self.V_Label_place)
        self.H_Label_Enter_place.addLayout(self.V_Enter_place)

        #之后再整体排布，使得按钮在整个的最下边
        self.V_Whole_place.addLayout(self.H_Label_Enter_place)
        self.V_Whole_place.addLayout(self.H_RadioButton_place)
        self.V_Whole_place.addLayout(self.H_Button_place)
        self.setLayout(self.V_Whole_place)
    #将Vicewindow与主界面串联起来

    def display_Window_func(self):
        self.display_Window.show()

    def Login_account(self):
        #检查输入时的账号密码
        global Username_input
        Username_input = self.Username_enter.text()
        Password_input = self.Password_enter.text()
        #如果登陆时的用户名存在，进一步检查密码是否合适
        if Username_input in Username_dic.keys():
            #账号和密码匹配时，提示登陆成功；如果账号密码不匹配，弹出错误窗口，提示错误
            if Username_dic[Username_input] == Password_input:
                QMessageBox.information(self, 'Login', 'Success')
                #弹出下一个显示信息的窗口
                self.display_Window = Display()
                self.display_Window_func()
                self.close()
                #建立对应的文件，如果已经存在，则打开文件
                file = open('{}.txt'.format(Username_input), 'a+')
                file.close()
            else:
                QMessageBox.critical(self, 'Error', 'Wrong password')
        #用户名不存在，弹出错误窗口
        else:
            QMessageBox.critical(self, 'Error', 'Username does not exist')


class Display(QWidget):
    def __init__(self):
        # super()表示新的MainWindow类是从Qwidget中继承而来的
        super(Display, self).__init__()
        self.initui()

    def initui(self):
        self.setWindowTitle('Your precious password')
        font = QFont('Microsoft YaHei', 12)
        #设置字体为加粗类型
        font.setBold(True)
        self.setWindowIcon(QIcon('BUAA.jpg'))
        QLabel.setFont(self, QFont('Microsoft YaHei', 15))
        self.setGeometry(400, 400, 700, 700*0.618)
        self.Info_Label = QLabel('The account name that has been saved:', self)
        #利用Qtextbroswer控件显示信息
        self.Account_info_show = QTextBrowser(self)
        #建立按钮，并与对应的槽函数相连接
        self.Button_Add_table = QPushButton('Add new', self)
        self.Button_Add_table.clicked.connect(self.ViceWindow_func)
        self.Button_Del_table = QPushButton('Delete', self)
        self.Button_Del_table.clicked.connect(self.Delete_func)
        self.Button_Refresh_table = QPushButton('Refresh', self)
        self.Button_Refresh_table.clicked.connect(self.Refresh_func)
        self.Button_Search_table = QPushButton('Search', self)
        self.Button_Search_table.clicked.connect(self.SearchWindow_func)
        self.Button_Info_change = QPushButton('Info change', self)
        self.Button_Info_change.clicked.connect(self.Info_change_func)
        self.ViceWindow_show = ViceWindow()
        #调用函数
        self.Info_show()
        self.Refresh_func()

    def ViceWindow_func(self):
        #与添加信息的窗口连接
        self.ViceWindow_show.show()

    def Info_show(self):
        #txt中的文件进行读写
        files = open('{}.txt'.format(Username_input), 'a+')
        Info_addup = ''
        List_info_individual = files.readlines()
        #因为我们加入到txt文件中的都是字符串类型，以空格分隔，所以我们要把字符串转化为可以正常显示的形式。
        for i in List_info_individual:
            i = i[:-1]
            i = i.split()
            Info_addup += decrypt(i[0])+'\n'
        self.Account_info_show.setText(Info_addup)
        files.close()
        #放置添加信息界面中的布局
        self.Place()

    def Place(self):
        self.Place_Button_table = QVBoxLayout()

        self.Place_Button_table.addWidget(self.Button_Search_table)
        self.Place_Button_table.addWidget(self.Button_Add_table)
        self.Place_Button_table.addWidget(self.Button_Refresh_table)
        self.Place_Button_table.addWidget(self.Button_Info_change)
        self.Place_Button_table.addWidget(self.Button_Del_table)

        self.Place_Label = QVBoxLayout()
        self.Place_Label.addWidget(self.Info_Label)
        self.Place_Label.addWidget(self.Account_info_show)

        self.Place_all_table_window = QHBoxLayout()

        self.Place_all_table_window.addLayout(self.Place_Label)
        self.Place_all_table_window.addLayout(self.Place_Button_table)
        self.setLayout(self.Place_all_table_window)

    def Refresh_func(self):
        files = open('{}.txt'.format(Username_input), 'r')
        Info_addup = ''
        List_info_individual = files.readlines()
        for i in List_info_individual:
            i = i[:-1]
            i = i.split()
            Info_addup += decrypt(i[0])+'\n'
        self.Account_info_show.setText(Info_addup)

    def Delete_func(self):
        #textCursor().selectedText()相当于事件，我们进行选中文本的操作之后，点击对应的按钮，就会实现对应的功能
        selected_info = self.Account_info_show.textCursor().selectedText()
        files = open('{}.txt'.format(Username_input), 'r')
        List_store_account = []
        List_info_individual = files.readlines()

        for i in List_info_individual:
            variable = i[:-1]
            variable = variable.split()
            List_store_account.append(decrypt(variable[0]))
        if selected_info == '':
            QMessageBox.critical(
                self, 'Error', 'The selected target is empty!')
        else:
            if selected_info in List_store_account:
                position = List_store_account.index(selected_info)
                if position != len(List_info_individual)-1:

                    List_info_individual = List_info_individual[:position] + \
                        List_info_individual[position+1:]
                else:
                    List_info_individual = List_info_individual[:-1]
            else:
                QMessageBox.critical(
                    self, 'Error', 'The target account doesn\'t exist')
        files.close()
        files_after_delete = open('{}.txt'.format(Username_input), 'w+')

        for i in List_info_individual:
            files_after_delete.write('{}'.format(i))
        List_info_individual_after_delete = files_after_delete.readlines()
        files_after_delete.close()
        #每次删除一个账户名之后，要进行一个刷新操作
        self.Refresh_func()

    def SearchWindow_func(self):
        #搜索功能的实现
        global Search_text
        Search_text = self.Account_info_show.textCursor().selectedText()
        #会弹出一个新的窗口，显示我们选中的信息
        self.SearchWindow_show = SearchWindow()
        self.SearchWindow_show.show()

    def Info_change_func(self):
        #这里实现信息更改的功能，可以分为删除旧信息，添加新信息的过程，所以我们可以直接调用Delete_func()和ViceWindow_func()
        #但是这样有个弊端，我们一旦进行更改信息的操作，旧的数据一定会被删除，没有反悔操作
        self.ViceWindow_func()
        self.Delete_func()
        self.Refresh_func()


class ViceWindow(QWidget):
    #ViceWindow()这个类就是新增信息的功能界面
    def __init__(self):
        super(ViceWindow, self).__init__()
        self.initui()

    def initui(self):
        self.setWindowTitle('Add Info')
        self.setGeometry(400, 400, 425, 425*0.618)
        self.setWindowIcon(QIcon('BUAA.jpg'))
        QLabel.setFont(self, QFont('Microsoft YaHei', 15))
        #创建按钮组件和文本输入框
        self.account_label = QLabel('Account', self)
        self.username1_label = QLabel('Username', self)
        self.password1_label = QLabel('Password', self)
        self.other_info_label = QLabel('Other Info')
        self.account_enter = QLineEdit(self)
        self.username1_enter = QLineEdit(self)
        self.password1_enter = QLineEdit(self)
        self.other_info_enter = QLineEdit(self)
        #同样设定为默认情况下密码不可见
        self.password1_enter.setEchoMode(QLineEdit.Password)
        self.Button_AddInfo = QPushButton('Add', self)
        self.Button_CancleInfo = QPushButton('Cancel', self)
        self.Button_CancleInfo.clicked.connect(self.Cancle)
        self.Button_AddInfo.clicked.connect(self.Add)
        self.Button_show_password = QRadioButton('Show the password', self)
        self.Button_show_password.toggled.connect(self.Password_show)
        self.Place_init()

    def Place_init(self):
        #实现布局的操作，同样H为水平布局，V为垂直布局
        self.H_account_place = QHBoxLayout()
        self.H_username1_place = QHBoxLayout()
        self.H_password1_place = QHBoxLayout()
        self.H_other_info_place = QHBoxLayout()
        self.H_Button_place = QHBoxLayout()
        self.V_place = QVBoxLayout()
        self.H_account_place.addWidget(
            self.account_label, alignment=Qt.AlignLeft)
        self.H_account_place.addWidget(
            self.account_enter, alignment=Qt.AlignRight)
        self.H_username1_place.addWidget(
            self.username1_label, alignment=Qt.AlignLeft)
        self.H_username1_place.addWidget(
            self.username1_enter, alignment=Qt.AlignRight)
        self.H_password1_place.addWidget(
            self.password1_label, alignment=Qt.AlignLeft)
        self.H_password1_place.addWidget(
            self.password1_enter, alignment=Qt.AlignRight)
        self.H_other_info_place.addWidget(
            self.other_info_label, alignment=Qt.AlignLeft)
        self.H_other_info_place.addWidget(
            self.other_info_enter, alignment=Qt.AlignRight)
        self.H_Button_place.addWidget(self.Button_AddInfo)
        self.H_Button_place.addWidget(self.Button_CancleInfo)
        self.H_Button_place.addWidget(self.Button_show_password)
        self.V_place.addLayout(self.H_account_place)
        self.V_place.addLayout(self.H_username1_place)
        self.V_place.addLayout(self.H_password1_place)
        self.V_place.addLayout(self.H_other_info_place)
        self.V_place.addLayout(self.H_Button_place)
        self.setLayout(self.V_place)

    def Password_show(self):
        #密码的明暗转换
        if self.Button_show_password.isChecked():
            self.password1_enter.setEchoMode(QLineEdit.Normal)
        else:
            self.password1_enter.setEchoMode(QLineEdit.Password)

    def Add(self):
        '''global Account_info,Username_info,Password_info,Other_info,List_info'''
        Account_info = self.account_enter.text()
        Username_info = self.username1_enter.text()
        Password_info = self.password1_enter.text()
        Other_info = self.other_info_enter.text()
        List_info = [Account_info, Username_info, Password_info, Other_info]
        file = open('{}.txt'.format(Username_input), 'a')
        if Other_info != '':
            #如果有备注的话，就添加进去，所有信息都要经过encrypt()处理，加密后写入txt文件中
            file.write('{} '.format(encrypt(Account_info)))
            file.write('{} '.format(encrypt(Username_info)))
            file.write('{} '.format(encrypt(Password_info)))
            file.write('{}\n'.format(encrypt(Other_info)))
        else:
            #如果没有备注，就标记为None
            Other_info1 = 'None'
            file.write('{} '.format(encrypt(Account_info)))
            file.write('{} '.format(encrypt(Username_info)))
            file.write('{} '.format(encrypt(Password_info)))
            file.write('{}\n'.format(encrypt(Other_info1)))
        file.close()
        self.account_enter.clear()
        self.username1_enter.clear()
        self.password1_enter.clear()
        self.other_info_enter.clear()
        self.close()

    def Cancle(self):
        #如果点击Cancle按钮，就关闭窗口返回即可
        self.close()
        

class SearchWindow(QWidget):
    #独立弹出的显示信息的窗口
    def __init__(self, Position=None):
        super(SearchWindow, self).__init__()
        self.initUi()

    def initUi(self, Position=None):
        QLabel.setFont(self, QFont('Microsoft YaHei', 15))
        self.setWindowTitle('Search Box')
        self.setWindowIcon(QIcon('BUAA.jpg'))
        self.setGeometry(600, 500, 400, 400*0.618)
        self.Info_Password_show = QTextBrowser(self)
        #这是对数据进行处理的操作，并显示在新建的QTextBrowser中
        files = open('{}.txt'.format(Username_input), 'r')
        List_store_account = []
        List_info_individual = files.readlines()
        for i in List_info_individual:
            variable = i[:-1]
            variable = variable.split()
            List_store_account.append(decrypt(variable[0]))
        if Position == '':
            pass
        else:
            if Search_text in List_store_account:
                Position = List_store_account.index(Search_text)
                Info_inneed = List_info_individual[Position]
                Info_inneed = Info_inneed[:-1]
                Info_inneed = Info_inneed.split()
                Output_in_Search_box = 'Username: '+decrypt(Info_inneed[1])+'\n'+'Password: '+decrypt(
                    Info_inneed[2])+'\n'+'Notes: '+decrypt(Info_inneed[3])
                self.Info_Password_show.setText(Output_in_Search_box)
            else:
                if Search_text == '':
                    self.Info_Password_show.setText(
                        'The selected target is empty!')
                else:
                    self.Info_Password_show.setText(
                        'The target account doesn\'t exist')
        self.Place()
        files.close()

    def Place(self):
        self.Place_Search_box = QHBoxLayout()
        self.Place_Search_box_all = QVBoxLayout()
        self.Place_Search_box.addWidget(self.Info_Password_show)
        self.Place_Search_box_all.addLayout(self.Place_Search_box)
        self.setLayout(self.Place_Search_box_all)


if __name__ == '__main__':
    #其实这一行不加也可以正常运行，因为我们在这个程序中没有调用别的文件。
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())
